package ws.very.util.play2

import play.api.mvc.RequestHeader
import play.api.libs.iteratee.Concurrent
import play.api.libs.iteratee.Concurrent._
import scala.reflect.ClassTag
import play.api.libs.iteratee.Enumerator
import play.api.libs.iteratee.Iteratee
import scala.concurrent.ExecutionContext
import ws.very.util.lang.ReturnSelfTrait
import java.util.HashMap
import java.util.UUID
import scala.collection.convert.WrapAsScala
import java.util.concurrent.ConcurrentHashMap

trait ChatPark[TakingObj, Msg] extends CommonUserPark[Msg] {
  private type T = this.type
  var inChatList: B = false
  class Taking {
    var cache = scala.collection.mutable.Set[TakingObj]()
    def all = cache
    def +(obj: TakingObj): T = rethis {
      cache += obj
    }
    def -(obj: TakingObj): T = rethis {
      cache -= obj
    }
  }
  def isTalkingWith(to: TakingObj) = talking.all.contains(to)
  def add2Talking(to: TakingObj) = talking + to
  def rmTaking(to: TakingObj) = talking - to

  private val talking = new Taking
}

trait CommonUserPark[T] extends ReturnSelfTrait {
  val sessionId: S
  val channel: Channel[T]
  def push(msg: T) = channel.push(msg)
}

trait BaseConnBiz[Msg, Park <: CommonUserPark[Msg]] {
  type MsgChannel = Channel[Msg]
  def saveNewSession(channel: MsgChannel, sessionId: S = UUID.randomUUID().toString()): S
  def disSession(sessionId: S)
}
trait CommonConnBiz[Msg, Park <: CommonUserPark[Msg]] extends BaseConnBiz[Msg, Park] {
  protected def newPark(session: S, channel: MsgChannel): Park

  private val userParkCache = new ConcurrentHashMap[S, Park]
  def saveNewSession(channel: MsgChannel, sessionId: S = UUID.randomUUID().toString()) = {
    userParkCache.put(sessionId, newPark(sessionId, channel))
    sessionId
  }

  def disSession(sessionId: S) =
    userParkCache.remove(sessionId)
  def userParkOption(sessionId: S) = Option(userParkCache.get(sessionId))
  def parkMap = WrapAsScala.mapAsScalaMap(userParkCache).toMap
  def allParks = parkMap.values
}

object Websockets {

  def frame[MsgType](inFn: Channel[MsgType] => Iteratee[MsgType, U]) = {
    val (sender, sendChannel) = Concurrent.broadcast[MsgType]
    inFn(sendChannel) -> sender
  }

  def frame2[MsgType](action: Channel[MsgType] => MsgType => U)(implicit ec: ExecutionContext) =
    frame[MsgType] { channel =>
      val caseFn = action(channel)
      Iteratee.foreach[MsgType](caseFn)
    }
  def frame4[MsgType](action: Channel[MsgType] => (MsgType => U, U => U))(implicit ec: ExecutionContext) =
    frame[MsgType] { channel =>
      val (caseFn, endFn) = action(channel)
      Iteratee.foreach[MsgType](caseFn).map(endFn)
    }

  def frame3[MsgType](action: RequestHeader => Channel[MsgType] => (MsgType => U) => Iteratee[MsgType, U])(implicit ec: ExecutionContext) = {
    req: RequestHeader =>
      fn: (MsgType => U) =>
        val (sender, sendChannel) = Concurrent.broadcast[MsgType]
        Iteratee.foreach[MsgType](fn) -> sender
  }
  //  (req: RequestHeader)(f: MsgType => Unit)(implicit ec: ExecutionContext) =
  //    frame(req) { channel: Channel[MsgType] =>
  //      Iteratees.eachWithEx[MsgType](f)(exFn)
  //    }

}